﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SaveTheQueen.Actions;
using System;

namespace SaveTheQueen.Component
{
    public abstract class Sprite : GameObject
    {
        private Vector2 position;
        private Vector2 offset;
        private Vector2 size;
        private Vector2 scale;
        protected Vector2 innerScale;
        private Vector2 center;
        protected Vector2 realCenter;
        protected Vector2 centerOffset;
        private Vector2 speed;
        private Color tint;
        private SpriteEffects spriteEffect;
        private Effect shader;
        private float rotation;
        private float depth;

        /// <summary>
        /// Position which the sprite is drawn.
        /// </summary>
        public Vector2 Position
        {
            get { return position; }
            set { position = value; }
        }
        /// <summary>
        /// Offset to the position. Use it to make minor adjustments to the position.
        /// </summary>
        public Vector2 Offset
        {
            get { return offset; }
            set { offset = value; }
        }
        /// <summary>
        /// Size of the sprite. It can be changed by the "Scale" property.
        /// </summary>
        public virtual Vector2 Size
        {
            get { return size; }
            set
            {
                size = value;
                CalcScaleFactor(value);
                CalcCenterAndCenterOffset(sourceSize());
            }
        }
        /// <summary>
        /// Scale of the sprite drawing.
        /// </summary>
        public Vector2 Scale
        {
            get { return scale; }
            set { scale = value; }
        }
        /// <summary>
        /// Center point of the rotation. Must be between 0 and 1.
        /// </summary>
        public Vector2 Center
        {
            get { return center; }
            set
            {
                center = value;
                center.X = (center.X > 1) ? center.X = 1 : (center.X < 0) ? center.X = 0 : center.X;
                center.Y = (center.Y > 1) ? center.Y = 1 : (center.Y < 0) ? center.Y = 0 : center.Y;
                CalcCenterAndCenterOffset(sourceSize());
            }
        }
        /// <summary>
        /// Speed of the sprite
        /// </summary>
        public virtual Vector2 Speed
        {
            get { return speed; }
            set { speed = value; }
        }
        /// <summary>
        /// Color that is applied to the sprite, defaut is Color.White.
        /// </summary>
        public Color Tint
        {
            get { return tint; }
            set { tint = value; }
        }
        /// <summary>
        /// Effect applied to the sprite.
        /// </summary>
        public SpriteEffects Effect
        {
            get { return spriteEffect; }
            set { spriteEffect = value; }
        }
        /// <summary>
        /// Shader applied to the sprite.
        /// </summary>
        public Effect Shader
        {
            get { return shader; }
            set { shader = value; }
        }
        /// <summary>
        /// Rotation (in radians) that the sprite is rotated.
        /// </summary>
        public float Rotation
        {
            get { return rotation; }
            set { rotation = value; }
        }
        /// <summary>
        /// Depth of the sprite, it is used to control the positioning. 0 means it will be drawn in front of all other sprites, 
        /// 1 means it will be behind every sprite. Must be between 0 and 1.
        /// </summary>
        public float Depth
        {
            get { return depth; }
            set { depth = value; depth = (depth > 1) ? 1 : (depth < 0) ? 0 : depth; }
        }

        protected abstract Vector2 sourceSize();
        protected void CalcScaleFactor(Vector2 desiredSize)
        {
            Vector2 source = sourceSize();
            if (source != Vector2.Zero)
                innerScale = desiredSize / source;
            else
                innerScale = Vector2.Zero;
        }
        protected void CalcCenterAndCenterOffset(Vector2 sourceSize)
        {
            realCenter = sourceSize * center;

            centerOffset = (Size * Scale) * Center;
        }


        public Sprite(int x, int y, int w, int h, GameObject owner, Game game)
            : base(owner, game)
        {
            position = new Vector2(x, y);
            offset = Vector2.Zero;
            size = new Vector2(w, h);
            scale = Vector2.One;
            center = new Vector2(0.5f, 0.5f);
            speed = Vector2.Zero;
            tint = Color.White;
            spriteEffect = SpriteEffects.None;
            shader = null;
            rotation = 0;
            depth = 0.5f;
        }

        protected override void LoadContent()
        {
            CalcScaleFactor(size);
            CalcCenterAndCenterOffset(sourceSize());
            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            Vector2 variation = speed * (float)gameTime.ElapsedGameTime.TotalSeconds;

            Position += variation;
            for (int i = 0; i < this.Components.Count; i++)
            {
                ((Sprite)this.Components[i]).Position += variation;
            }

            base.Update(gameTime);
        }

        public Vector4BlendAction Fade(Color input, Color output, float time)
        {
            Vector4BlendAction ba = new Vector4BlendAction(Game);
            ba.Method = InterpolationMethod.Linear;
            ba.Start(input.ToVector4(), output.ToVector4(), 1f);
            ba.OnUpdate += delegate()
            {
                tint = new Color(ba.Actual);
            };
            ba.OnFinished += delegate()
            {
                DestroyAction(ba);
            };
            this.Actions.Add(ba);
            return ba;
        }
        public Vector4BlendAction Flash(Color flashColor, float time)
        {
            Vector4BlendAction ba = new Vector4BlendAction(Game);
            ba.Method = InterpolationMethod.Linear;
            ba.Start((Color.White).ToVector4(), flashColor.ToVector4(), time);
            ba.OnUpdate += delegate()
            {
                tint = new Color(ba.Actual);
            };
            ba.OnFinished += delegate()
            {
                ba.ClearEvents();

                ba.Start(flashColor.ToVector4(), (Color.White).ToVector4(), time);
                ba.OnUpdate += delegate()
                {
                    tint = new Color(ba.Actual);
                };
                ba.OnFinished += delegate()
                {
                    DestroyAction(ba);
                };
            };
            this.Actions.Add(ba);
            return ba;
        }
        public PeriodicAction Shake(float amount, float frequency, float time)
        {
            int shakeCount = (int)(time / frequency);
            int counter = 0;

            System.Diagnostics.Debug.WriteLine("");
            PeriodicAction pa = new PeriodicAction(Game);
            pa.IsLopping = true;
            pa.Start(0, amount, frequency);
            pa.OnUpdate += delegate()
            {
                offset.X = pa.Actual;
            };
            pa.OnFinished += delegate()
            {
                counter++;
                if (counter > shakeCount)
                {
                    DestroyAction(pa);
                    offset = Vector2.Zero;
                }
            };
            this.Actions.Add(pa);
            return pa;
        }
    }
}
